package snapex;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@AutoConfigureAfter(EncryptablePropertiesConfiguration.class)
public class ProxyConfiguration {

	@Value("${snapex.proxy.enabled}")
	private boolean isProxyEnabled;
	
	@Value("${snapex.proxy.host}")
	private String proxyHost;
	
	@Value("${snapex.proxy.port}")
	private Integer proxyPort;
	
	@Value("${snapex.proxy.user}")
	private String proxyUser;
	
	@Value("${snapex.proxy.password}")
	private String proxyPassword;
	
	@Value("${snapex.readTimeout}")
	private Integer readTimeout;
	
	@Value("${snapex.connectTimeout}")
	private Integer connectTimeout;
		
	private SimpleClientHttpRequestFactory httpClientFactory() {
		
		SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
		
		factory.setConnectTimeout(connectTimeout);
		
		factory.setReadTimeout(readTimeout);
		
		log.debug("****isProxyEnabled={}, proxyHost={}, proxyPort={}",isProxyEnabled,proxyHost, proxyPort);
		
		if(isProxyEnabled) {
			Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost,proxyPort));
			factory.setProxy(proxy);
		}
		
		return factory;
	}
	
	@Bean
	public RestTemplate restTemplate() {
	    return new RestTemplate(httpClientFactory()) {

			@Override
			public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
					Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
			
				return super.exchange(url, method, proxyHttpEntity(requestEntity), responseType, uriVariables);
			}

			@Override
			public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
					Class<T> responseType) throws RestClientException {

				return super.exchange(url, method, proxyHttpEntity(requestEntity), responseType);
			}

			@Override
			public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
					ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables)
					throws RestClientException {

				return super.exchange(url, method, proxyHttpEntity(requestEntity), responseType, uriVariables);
			}

			@Override
			public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
					ParameterizedTypeReference<T> responseType) throws RestClientException {

				return super.exchange(url, method, proxyHttpEntity(requestEntity), responseType);
			}

			@Override
			public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
					Class<T> responseType, Object... uriVariables) throws RestClientException {
												
				return super.exchange(url, method, proxyHttpEntity(requestEntity), responseType, uriVariables);
			}

			@Override
			public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
					ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
						
				return super.exchange(url, method, proxyHttpEntity(requestEntity), responseType, uriVariables);
			}
			
			private HttpEntity proxyHttpEntity(HttpEntity httpEntity) {
				
				HttpEntity<?> proxyHttpEntity = httpEntity;
				
				if(isProxyEnabled) {
					
					HttpHeaders proxyHeaders = new HttpHeaders();
					
					proxyHeaders.addAll(httpEntity.getHeaders());
					
					proxyHeaders.add("Proxy-Authorization", "Basic " + Base64Utils.encodeToString((proxyUser+":"+proxyPassword).getBytes()));
					
					proxyHttpEntity = new HttpEntity(httpEntity.getBody(),proxyHeaders);
				}				
				
				return proxyHttpEntity;
			}
	    };
	}
}
